package com.uobgroup.ews.uam.core.service;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.uobgroup.ews.uam.core.common.UMMConstants;
import com.uobgroup.ews.uam.core.configuration.AppConfig;
import com.uobgroup.ews.uam.core.dao.AccessControlDao;
import com.uobgroup.ews.uam.core.dao.CombinationACLDao;
import com.uobgroup.ews.uam.core.dao.CombinationMatrixDao;
import com.uobgroup.ews.uam.core.dao.SecurityGroupDao;
import com.uobgroup.ews.uam.core.domain.AccessControl;
import com.uobgroup.ews.uam.core.domain.CombinationACL;
import com.uobgroup.ews.uam.core.domain.CombinationMatrix;
import com.uobgroup.ews.uam.core.domain.CombinationType;
import com.uobgroup.ews.uam.core.domain.Reference;
import com.uobgroup.ews.uam.core.domain.SecurityGroup;

@Service
@Transactional
public class CombinationMatrixServiceImpl implements CombinationMatrixService {

   @Autowired
   CombinationMatrixDao combinationMatrixDao;

   @Autowired
   AccessControlDao accessControlDAO;

   @Autowired
   SecurityGroupDao securityGroupDAO;

   @Autowired
   CombinationACLDao combinationACLDao;

   @Override
   public void save(CombinationMatrix combinationMatrix) {
      this.combinationMatrixDao.save(combinationMatrix);
      findACLsToMakeAssociate(combinationMatrix);
   }

   public long count() {
      long count = this.combinationMatrixDao.count();
      return count;
   }

   public List<CombinationMatrix> findAll() {
      List<CombinationMatrix> combinationMatrixs = combinationMatrixDao.findAll();
      return combinationMatrixs;
   }

   public List<CombinationMatrix> findEntries(int firstResult, int maxResults) {
      List<CombinationMatrix> combinationMatrixs = combinationMatrixDao.findAll(firstResult, maxResults);
      return combinationMatrixs;
   }

   @Override
   public CombinationMatrix findbyId(Long id) {
      CombinationMatrix combinationMatrix = combinationMatrixDao.findOne(id);
      return combinationMatrix;
   }

   /*
    * Phuong Tran Update the existing CombinationMatrix entity if isn't assigned
    * to a profile
    */
   @Override
   public void update(CombinationMatrix combinationMatrix) {
      String userId = combinationMatrix.getLastModificationUserId();
      if (!combinationMatrixDao.checkTheAssociationWithProfile(combinationMatrix.getId())) {
         boolean isCombinationTypeChanged = false;
         // get the existing entity and set the new values to it
         CombinationMatrix existingEntity = combinationMatrixDao.findOne(combinationMatrix.getId());

         if (existingEntity != null) {
            if (!combinationMatrix.getCombinationType().toString()
                  .equals(existingEntity.getCombinationType().toString())) {
               existingEntity.setCombinationType(combinationMatrix.getCombinationType());
               List<CombinationACL> combinationACLs = existingEntity.getAccessControls();
               // remove CombinationACLs
               for(CombinationACL acl : combinationACLs){
                  acl.setLastModificationUserId(userId);
               }
               combinationMatrixDao.removeCombinationACLs(combinationACLs);
               isCombinationTypeChanged = true;
            }
         }

         CombinationMatrix updatedEntity = combinationMatrixDao.update(combinationMatrix);
         updatedEntity.setLastModificationUserId(userId);
         if (isCombinationTypeChanged) {
            findACLsToMakeAssociate(updatedEntity);
         }
         else {
            updateSecurityGroup(combinationMatrix);
         }
      }
   }

   /*
    * Phuong Tran Delete the specify Id of Combination Matrix that isn't
    * assigned to a profile
    */
   @Override
   public void deleteById(Long id) {
      // first check if this Combination Matrix is assigned to a Profile
      if (!combinationMatrixDao.checkTheAssociationWithProfile(id)) {
         CombinationMatrix delEntity = combinationMatrixDao.findOne(id);
         if (delEntity != null) {
            List<SecurityGroup> lstSecGroup = getSecurityGroupList(delEntity);
            // delete
            combinationMatrixDao.delete(delEntity);
            // remove security group
            removeSecurityGroups(lstSecGroup);
         }
      }
      else {
         // should return a message to warning can not delete the Combination
         // Matrix
         // because it is assigned to a existing profile
      }
   }
   
   @Override
   public void delete(CombinationMatrix combinationMatrix){
      if (!combinationMatrixDao.checkTheAssociationWithProfile(combinationMatrix.getId())) {
         String userId = combinationMatrix.getLastModificationUserId();
         for(CombinationACL acl : combinationMatrix.getAccessControls()){
            acl.setLastModificationUserId(userId);
         }
         List<SecurityGroup> lstSecGroup = getSecurityGroupList(combinationMatrix);
         combinationMatrixDao.delete(combinationMatrix);
         removeSecurityGroups(lstSecGroup);
      }
   }

   /*
    * Phuong Tran
    */
   public void findACLsToMakeAssociate(CombinationMatrix combination) {
      // build security groups from this combination and all ACLs that have
      // the same type
      String userId = combination.getLastModificationUserId();
      CombinationType combinationType = combination.getCombinationType();
      // search the AccessControl table the ACLs that have type =
      // combinationType
      List<AccessControl> lstACL = accessControlDAO.findACLbyType(combinationType.ordinal());
      Iterator<AccessControl> it = lstACL.iterator();
      while (it.hasNext()) {
         AccessControl acl = it.next();
         acl.setLastModificationUserId(userId);
         SecurityGroup securityGroup = buildSecurityGroup(combination, acl);
         if (securityGroup != null) {
            buildCombinationACL(combination, acl, securityGroup);
         }
      }
   }

   /*
    * Phuong Tran build and persist to DB a security groups record
    */
   public SecurityGroup buildSecurityGroup(CombinationMatrix combination, AccessControl acl) {
      SecurityGroup securityGroup = new SecurityGroup();
      String userId = acl.getLastModificationUserId();
      if (combination != null && acl != null) {
         String name = buildSecGroupName(combination, acl);
         
         //TODO : confirm with Julian
         if(name != null && name.length() > 64){
            name = name.substring(0, 64);
         }
         
         securityGroup.setDisplayName(name);
         if(securityGroup.getDisplayName() != null){
            if(securityGroup.getDisplayName().contains(" ")){
               securityGroup.setDisplayName(securityGroup.getDisplayName().replace(" ", "_").trim());
            }
         }
         //securityGroup.setDN(name);
         //securityGroup.setDN(AppConfig.getProperty(UMMConstants.LDAP_DN_GROUP_PREFIX));
         securityGroup.setDescription(name);
         securityGroup.setLastModificationUserId(userId);
         /*securityGroupDAO.save(securityGroup);

         securityGroup = securityGroupDAO.findName(name);
         if (securityGroup == null) {
            securityGroup = new SecurityGroup();
         }*/
      }
      return securityGroup;
   }

   /*
    * Phuong Tran
    */
   public String buildSecGroupName(CombinationMatrix combination, AccessControl acl) {
      String name = "acl";
      name = name + "_" + acl.getName();
      if (combination.getCombinationType() != null)
         name = name + "_" + combination.getCombinationType().toString();
      if (combination.getDocumentType() != null)
         name = name + "_" + combination.getDocumentType().getCode();
      if (combination.getProcessingCentre() != null)
         name = name + "_" + combination.getProcessingCentre().getCode();
      if (combination.getTransactionType() != null)
         name = name + "_" + combination.getTransactionType().getCode();
      if (combination.getProductType() != null)
         name = name + "_" + combination.getProductType().getCode();
      if (combination.getUserLocation() != null)
         name = name + "_" + combination.getUserLocation().getCode();
      if (combination.getCountry() != null)
         name = name + "_" + combination.getCountry().getCode();
      return name;
   }

   /*
    * Phuong Tran build a combination ACL record and persist to DB
    */
   public void buildCombinationACL(CombinationMatrix combination, AccessControl acl, SecurityGroup securityGroup) {
      // save record to CombinationACL table
      String userId = acl.getLastModificationUserId();
      CombinationACL entity = new CombinationACL();
      entity.setCombination(combination);
      entity.setAccessControl(acl);
      securityGroup.setDN(AppConfig.getProperty(UMMConstants.LDAP_DN_GROUP_PREFIX + entity.getParentGroupName()));
      entity.setSecurityGroup(securityGroup);
      entity.setLastModificationUserId(userId);
      combinationACLDao.save(entity);
   }

   @Override
   public List<CombinationMatrix> findByTypeAndDescription(CombinationType type, String description) {
      return combinationMatrixDao.findByTypeAndDescription(type, description);
   }

   @Override
   public boolean isCombinationInUse(Long Id) {
      return combinationMatrixDao.checkTheAssociationWithProfile(Id);
   }

   /*
    * Phuong Tran
    */
   @Override
   public boolean isPropertiesExisted(CombinationMatrix combinationMatrix) {
      return combinationMatrixDao.isPropertiesExisted(combinationMatrix);
   }

   /*
    * Phuong Tran
    */
   private void updateSecurityGroup(CombinationMatrix combination) {
      String userId = combination.getLastModificationUserId();
      List<AccessControl> lstACLs = combinationACLDao.findACLs(combination);
      if (lstACLs != null) {
         Iterator<AccessControl> it = lstACLs.iterator();
         while (it.hasNext()) {
            AccessControl acl = it.next();
            SecurityGroup secGroup = combinationACLDao.findSecGroup(combination, acl);
            if (secGroup != null) {
               String secName = buildSecGroupName(combination, acl);
               secGroup.setDisplayName(secName);
               secGroup.setLastModificationUserId(userId);
               securityGroupDAO.update(secGroup);
            }
         }
      }
   }

   /*
    * Phuong Tran
    */
   private List<SecurityGroup> getSecurityGroupList(CombinationMatrix combination) {
      String userId = combination.getLastModificationUserId();
      List<SecurityGroup> lstSecGroup = new ArrayList<SecurityGroup>();
      List<AccessControl> lstACLs = combinationACLDao.findACLs(combination);
      if (lstACLs != null) {
         Iterator<AccessControl> it = lstACLs.iterator();
         while (it.hasNext()) {
            AccessControl acl = it.next();
            SecurityGroup secGroup = combinationACLDao.findSecGroup(combination, acl);
            if (secGroup != null) {
               secGroup.setLastModificationUserId(userId);
               lstSecGroup.add(secGroup);
            }
         }
      }
      return lstSecGroup;
   }

   /*
    * Phuong Tran
    */
   private void removeSecurityGroups(List<SecurityGroup> lstSecGroup) {
      Iterator<SecurityGroup> it = lstSecGroup.iterator();
      while (it.hasNext()) {
         SecurityGroup secGroup = it.next();
         if (secGroup != null) {
            securityGroupDAO.delete(secGroup);
         }
      }
   }
   
   /**
    * @author dongn
    */
   @Override
   public long countCombinationMatrixsThatHasCountryReference(Reference country) {
      return combinationMatrixDao.countCombinationMatrixsThatHasCountryReference(country);
   }

   @Override
   public long countCombinationMatrixsThatHasDocumentTypeReference(Reference documentType) {
      return combinationMatrixDao.countCombinationMatrixsThatHasDocumentTypeReference(documentType);
   }

   @Override
   public long countCombinationMatrixsThatHasProductTypeReference(Reference productType) {
      return combinationMatrixDao.countCombinationMatrixsThatHasProductTypeReference(productType);
   }

   @Override
   public long countCombinationMatrixsThatHasTransactionTypeReference(Reference transactionType) {
      return combinationMatrixDao.countCombinationMatrixsThatHasTransactionTypeReference(transactionType);
   }

   @Override
   public long countCombinationMatrixsThatHasProcessingCenterReference(Reference processingCentre) {
      return combinationMatrixDao.countCombinationMatrixsThatHasProcessingCenterReference(processingCentre);
   }

   @Override
   public long countCombinationMatrixsThatHasUserLocationReference(Reference userLocation) {
      return combinationMatrixDao.countCombinationMatrixsThatHasUserLocationReference(userLocation);
   }

}
